/*
 * 文件下载限流示例程序
 * 
 * https://www.yunkt.com/u100055/
 */
package com.kfayun.demo.ratelimit.web;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.google.common.util.concurrent.RateLimiter;
import com.kfayun.demo.ratelimit.AppConfig;
import com.kfayun.demo.ratelimit.impl.BandwidthLimitingOutputStream;

/**
 * 控制器类
 * @author Billy Zhang
 */
@RestController
public class HomeController {

	private static final Logger log = LoggerFactory.getLogger(HomeController.class);
	
	private static final int BLOCK_SIZE = 16384; // 16k
    private static final int BLOCK_PER_SECOND = 16;

    @Autowired
    private AppConfig config;

    /**
     * 下载项目upload目录的一个指定文件。
     * 
     * @param request
     * @param response
     */
    @GetMapping("down")
    public void down(
            HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        String fileName = "SpringBoot 2.5开发实战.pdf";

        Path path = Paths.get(config.getUploadPath(), fileName);
        if (!Files.exists(path)) {
            log.info("{} 文件不存在。", path);
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        } 
        
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "utf-8"));
        response.setContentType("application/octet-stream;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        
        long fileSize = Files.size(path);
        response.setContentLengthLong(fileSize);
        log.info("开始下载文件 {}, size: {}", path, fileSize);

        try (OutputStream outStream = new BufferedOutputStream(response.getOutputStream())) {
			
            long beginTime = System.currentTimeMillis();
            
            Files.copy(path, outStream);
			outStream.flush();

            long duration = System.currentTimeMillis() - beginTime;
            log.info("下载完成，用时：{}ms", duration);
            
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
		}
    }

    /**
     * 下载项目upload目录的一个指定文件。
     * 
     * @param request
     * @param response
     */
    @GetMapping("limitdown")
    public void limitDown(
            HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        String fileName = "SpringBoot 2.5开发实战.pdf";
	
        Path path = Paths.get(config.getUploadPath(), fileName);
        if (!Files.exists(path)) {
            log.info("{} 文件不存在。", path);
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        } 

        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "utf-8"));
        response.setContentType("application/octet-stream;charset=utf-8");
        response.setCharacterEncoding("utf-8");
    
        long fileSize = Files.size(path);
        response.setContentLengthLong(fileSize);
        log.info("开始下载文件 {}, size: {}", path, fileSize);

        try (OutputStream outStream = new BufferedOutputStream(response.getOutputStream())) {
            long beginTime = System.currentTimeMillis();
            
			// 限流 256k = BLOCK_PER_SECOND * BLOCK_SIZE
            log.info("当前限速为：{}", BLOCK_PER_SECOND * BLOCK_SIZE);

            // 创建限流类
			RateLimiter limiter = RateLimiter.create(BLOCK_PER_SECOND);
            // 输出
			try (OutputStream limitStream = new BandwidthLimitingOutputStream(outStream, limiter, BLOCK_SIZE)) {
				Files.copy(path, limitStream);
			}

			outStream.flush();

            long duration = System.currentTimeMillis() - beginTime;
            log.info("下载完成，用时：{}s", duration / 1000);
            
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
		}
    }

}
